<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        /* 默认值 */
        function setCountry(con = 'China') {
            return `I'm from ${con}.`
        }
        console.log(setCountry('Cuba'), "//", setCountry());

        // 解构&默认值结合
        function foo({ x, y = 5 } = {}) {
            console.log(x, y);
        }
        foo(); // undefined 5

        // length - 返回没有指定默认值的参数个数
        // 个数从指定默认值的最后一个参数往前计算
        console.log(foo.length); // 0 因为对象被指定了默认值空对象
        console.log((function (a, b, c = 0) { }).length); // 2

        // 默认参数作用域 - 参数指定默认值时存在
        {
            let x = 1;
            function fun(y = x) {
                let x = 4;
                console.log(y);
            }
            fun(); // 1
        }
        {
            var x = 1;
            function foo1(x, y = function () { x = 2; }) {
                var x = 3;
                // ↑这样赋值 x覆盖全局变量x 值为3
                // x = 3; 
                // ↑这样赋值 x指向第一个参数x 与匿名函数内部的x一致 值为2
                y();
                console.log(x);
            }
            foo1();
            console.log(x);
        }
        console.log("--------");

        // 应用 - 指定默认值让参数不能省略/可以省略
        // 不能省略
        function throwIfMissing() {
            throw new Error('Missing parameter');
        }
        function foo2(mustBeProvided = throwIfMissing()) {
            return mustBeProvided;
        }
        foo2(1); // 没有参数时抛出错误
        // 可以省略
        function foo3(optional = undefined) {
            return optional;
        }
        console.log(foo3());

        /* rest 参数 - 用于不确定参数个数时 只能是最后一个参数*/
        function push1(array, ...items) {
            items.forEach(function (items) {
                array.push(items);
            });
            console.log(array);
            console.log(items instanceof Array); // true
        }
        let arr = [];
        push1(arr, 1, 2, 3, 4);

        /* 箭头函数 */
        let f = v => v; // f - 函数名, 第一个v - 参数, 第二个v - 表达式, 默认返回
        console.log(f('111'));
        // 箭头函数与解构的结合
        let full = ({ first, last }) => first + ' & ' + last;
        let obj = {
            first: 'ff',
            last: 'll'
        }
        console.log(full(obj));
        // 箭头函数简化回调函数
        function push2(array, ...items) {
            items.forEach(items => array.push(items));
            console.log(array);
        }
        push2(arr, 5, 6, 7, 8);
        // 箭头函数的this指向, 箭头函数内部没有this对象, 内部的this指向上层作用域的this
        function Timer() {
            this.s1 = 0;
            this.s2 = 0;
            // 箭头函数
            setInterval(() => this.s1++, 1000);
            // 普通函数
            setInterval(function () {
                this.s2++;
            }, 1000);
        }
        var timer = new Timer();
        setTimeout(() => console.log('s1: ', timer.s1, 's2: ', timer.s2), 2000);
        // s1: 2, s2: 0; s1的this绑定在Timer函数作用域, s2的this绑定在timer对象运行时所在的作用域(全局), this不被更新

        globalThis.s = 21;
        const obj1 = {
            s: 42,
            m: () => console.log(this.s)
        };
        obj1.m() // 21
        // 箭头函数先在全局中定义, 再赋值给obj.m; 所以箭头函数中的this指向全局, 值为21

        /* 递归&尾递归优化 */
        // 阶乘的递归
        let factorial1 = (num) => num <= 1 ? 1 : num * factorial1(num - 1);
        // 阶乘的尾递归 total储存乘积 num递减
        let factorial2 = (num, total = 1) => num === 1 ? total : factorial2(num - 1, num * total);
        // 尾递归需要增加一个参数total, 第一眼很难理解为什么需要传入, 有两种解决方法
        // 1 - 增加一个函数
        {
            let factorial = (num) => tailFactorial(num, 1);
            let tailFactorial = (num, total) => num === 1 ? total : tailFactorial(num - 1, num * total);
            console.log(factorial(3));
        }
        // 2 - 柯里化
        {
            let currying = function(fn, n){
                return function(m) {
                    return fn.call(this, m, n);
                }
            };
            let tailFactorial = (num, total) => num === 1 ? total : tailFactorial(num - 1, num * total);
            let factorial = currying(tailFactorial, 1);
            console.log(factorial(4));
        }
        // 3 - 默认值 默认total = 1

    </script>
</body>

</html>